﻿using Opc.Ua;
using OpcUaHelper;
using PDB.DotNetSDK;
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading;

namespace OPCCollect
{
    public class CollectTask
    {
        private List<TagInfo> tagList;
        private OpcUaClient uaClient;
        private PDBConnection pdbConn;
        private Thread workThread;
        private bool running;

        public string DBTable { get; set; }

        public string OpcServer { get; set; }

        public string PdbServer { get; set; }

        public CollectTask(List<TagInfo> tagList)
        {
            this.tagList = tagList;
            uaClient = null;
            running = true;
            workThread = null;
            pdbConn = null;
        }

        public void RunTask()
        {
            workThread = new Thread(TaskWork);
            workThread.IsBackground = true;
            workThread.Start();
        }

        private void TaskWork()
        {
            while(running)
            {
                GetOpcValues();
                SaveOpcValues();
                Thread.Sleep(2000);
            }
        }


        private void GetOpcValues()
        {
            //首先将所有值清空
            foreach(TagInfo tag in tagList)
            {
                tag.OpcValue = null;
            }

            try
            {
                if (uaClient == null)
                {
                    uaClient = new OpcUaClient();
                    uaClient.ConnectServer(OpcServer).Wait();
                }

                const int BATCH = 1000;
                int batch = BATCH;
                for (int i = 0; i < tagList.Count; i += batch)
                {
                    batch = (tagList.Count - i) > BATCH ? BATCH : tagList.Count - i;
                    NodeId[] nodeIds = new NodeId[batch];
                    for (int j = 0; j < batch; j++)
                    {
                        //本地测试临时修改
                        nodeIds[j] = new NodeId(string.Format("ns=2;s={0}", tagList[i + j].OpcName));
                        //nodeIds[j] = new NodeId(string.Format("ns=2;s=S7-1500RH-System_1.PLC_1.DB000.{0}", tagList[i + j].OpcName));
                    }

					List<DataValue> dataList = uaClient.ReadNodes(nodeIds);
                    for (int j = 0; j < batch; j++)
                    {
                        tagList[i + j].OpcValue = dataList[j];
                    }
                }
            }
            catch(Exception ex)
            {
                uaClient = null;
            }
        }

        private void SaveOpcValues()
        {
            DataTable table = new DataTable(DBTable);
            table.Columns.Add(new DataColumn("devid", typeof(long)));
            table.Columns.Add(new DataColumn("tstamp", typeof(DateTime)));
            table.Columns.Add(new DataColumn("val", typeof(int)));
            table.Columns.Add(new DataColumn("quality", typeof(sbyte)));
            
            foreach(TagInfo tag in tagList)
            {
                if (tag.OpcValue != null && tag.OpcValue.Value != null)
                {
                    DataRow dr = table.NewRow();
                    dr["devid"] = tag.DevId;
                    dr["quality"] = DataValue.IsGood(tag.OpcValue) ? 0 : 1;

                    if (Math.Abs((DateTime.Now - tag.OpcValue.ServerTimestamp).TotalHours) < 24)
                    {
                        dr["tstamp"] = tag.OpcValue.ServerTimestamp;
                    }
                    else
                    {
                        dr["tstamp"] = DateTime.Now;
                    }
                    if (tag.OpcType == "real" || tag.OpcType == "int")
                    {
                        dr["val"] = Convert.ToInt32(Convert.ToDouble(tag.OpcValue.Value) * tag.Coefficient);
                    }
                    else if (tag.OpcType == "bool")
                    {
                        dr["val"] = Convert.ToBoolean(tag.OpcValue.Value) ? 1 : 0;
                    }
                    else
                    {
                        //unknow opc type
                    }

                    table.Rows.Add(dr);
                }
            }

            if (table.Rows.Count > 0)
            {
                try
                {
                    if (pdbConn == null)
                    {
                        pdbConn = new PDBConnection(PdbServer);
                        pdbConn.Open();
                    }

                    PDBCommand cmd = pdbConn.CreateCommand();
                    cmd.ExecuteInsert(table);
                }
                catch(Exception ex)
                {
                    pdbConn = null;
                }
            }
        }

    }
}
